/*
 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "device_resource_if.h"
#include "hdf_base.h"
#include "hdf_log.h"
#include "osal_mem.h"
#include "uart_core.h"
#include "fsl_common.h"
#include "fsl_clock.h"
#include "fsl_usart.h"

struct Lpc546xxUartHost {
    struct UartHost *host;
    uint32_t busNum;
    uint32_t baudRate;
    uint32_t clkRate;
    USART_Type *usart;
};

static int32_t Lpc546xxReadConfigFromHcs(struct Lpc546xxUartHost *luart, const struct DeviceResourceNode *node)
{
    struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);

    if (iface == NULL || iface->GetUint32 == NULL) {
        HDF_LOGE("%s: face is invalid", __func__);
        return HDF_ERR_NOT_SUPPORT;
    }

    if (iface->GetUint32(node, "busNum", &luart->busNum, 0) != HDF_SUCCESS) {
        HDF_LOGE("%s: read busNum fail", __func__);
        return HDF_ERR_IO;
    }

    if (iface->GetUint32(node, "baudRate", &luart->busNum, 0) != HDF_SUCCESS) {
        HDF_LOGE("%s: read baudRate fail", __func__);
        return HDF_ERR_IO;
    }

    return HDF_SUCCESS;
}

static int32_t Lpc546xxUartInit(struct Lpc546xxUartHost *luart)
{
    int32_t ret;
    USART_Type *usart = NULL;
    usart_config_t usart_config;
    struct UartHost *host = luart->host;
    const struct DeviceResourceNode *node = NULL;

    HDF_LOGI("%s: enter", __func__);
    if (host == NULL || host->device == NULL) {
        HDF_LOGE("%s: host or device null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    node = host->device->property;

    if (luart == NULL || luart->host == NULL || luart->host->device == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }

    if (host->device->property == NULL) {
        HDF_LOGE("%s: properity node null", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    ret = Lpc546xxReadConfigFromHcs(luart, host->device->property);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("Lpc546xxUartInit: read hcs config failed");
        return ret;
    }

    usart = USART_GetInstanceByNum(host->num);
    if (usart == NULL) {
        HDF_LOGE("%s: get usart:%u instance fail", __func__, host->num);
        return HDF_ERR_NOT_SUPPORT;
    }

    luart->clkRate = CLOCK_GetFreq(kCLOCK_Flexcomm0);
    USART_GetDefaultConfig(&usart_config);
    usart_config.baudRate_Bps = luart->baudRate;
    /* Enable clock and initial UART module follow user configure structure. */
    USART_Init(usart, &usart_config, luart->clkRate);
    luart->usart = usart; 

    return HDF_SUCCESS;
}

static int32_t Lpc546xxUartDeinit(struct UartHost *host)
{
    HDF_LOGI("%s: enter", __func__);

    if (host == NULL || host->priv == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    
    return HDF_SUCCESS;
}

static int32_t Lpc546xxUartRead(struct UartHost *host, uint8_t *data, uint32_t size)
{
    status_t ret;
    struct Lpc546xxUartHost *luart = NULL;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    luart = host->priv;
    ret = USART_ReadBlocking(luart->usart, data, size);
    if (ret != kStatus_Success) {
        HDF_LOGE("%s: read blocking fail:%d", ret);
        return HDF_FAILURE;
    }
    return size;
}

static int32_t Lpc546xxUartWrite(struct UartHost *host, uint8_t *data, uint32_t size)
{
    struct Lpc546xxUartHost *luart = NULL;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    luart = host->priv;

    USART_WriteBlocking(luart->usart, data, size);
    return size;
}

static int32_t Lpc546xxUartSetBaud(struct UartHost *host, uint32_t baudRate)
{
    status_t ret;
    struct Lpc546xxUartHost *luart = NULL;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    luart = host->priv;

    ret = USART_SetBaudRate(luart->usart, baudRate, luart->clkRate);
    if (ret != kStatus_Success) {
        HDF_LOGE("%s: set baud rate fail:%d", ret);
        return HDF_FAILURE;
    }
    luart->baudRate = baudRate;
    return HDF_SUCCESS;
}

static int32_t Lpc546xxUartGetBaud(struct UartHost *host, uint32_t *baudRate)
{
    struct Lpc546xxUartHost *luart = NULL;

    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or priv null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    luart = host->priv;
    *baudRate = luart->baudRate;

    return HDF_SUCCESS;
}

static int32_t Lpc546xxUartSetAttribute(struct UartHost *host, struct UartAttribute *attribute)
{
    HDF_LOGE("%s: not supported yet", __func__);
    return HDF_ERR_NOT_SUPPORT;
}

static int32_t Lpc546xxUartGetAttribute(struct UartHost *host, struct UartAttribute *attribute)
{
    HDF_LOGE("%s: not supported yet", __func__);
    return HDF_ERR_NOT_SUPPORT;
}

static int32_t Lpc546xxUartSetTransMode(struct UartHost *host, enum UartTransMode mode)
{
    HDF_LOGE("%s: not supported yet", __func__);
    return HDF_ERR_NOT_SUPPORT;
}

struct UartHostMethod g_uartHostMethod = {
    .Read = Lpc546xxUartRead,
    .Write = Lpc546xxUartWrite,
    .SetBaud = Lpc546xxUartSetBaud,
    .GetBaud = Lpc546xxUartGetBaud,
    .SetAttribute = Lpc546xxUartSetAttribute,
    .GetAttribute = Lpc546xxUartGetAttribute,
    .SetTransMode = Lpc546xxUartSetTransMode,
};

static int32_t HdfLpc546xxBind(struct HdfDeviceObject *device)
{
    HDF_LOGI("%s: entry", __func__);
    if (device == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    if (UartHostCreate(device) == NULL) {
        return HDF_FAILURE;
    }
    HDF_LOGI("%s: bind success!", __func__);
    return HDF_SUCCESS;
}

int32_t HdfLpc546xxInit(struct HdfDeviceObject *device)
{
    int32_t ret;
    struct UartHost *host = NULL;
    struct Lpc546xxUartHost *luart = NULL;

    HDF_LOGI("%s: entry", __func__);
    if (device == NULL) {
        HDF_LOGE("%s: device is null", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    host = UartHostFromDevice(device);
    if (host == NULL) {
        HDF_LOGE("%s: host is null", __func__);
        return HDF_FAILURE;
    }

    luart = (struct Lpc546xxUartHost *)OsalMemCalloc(sizeof(*luart));
    if (luart == NULL) {
        return HDF_ERR_MALLOC_FAIL;
    }
    luart->host = host;

    ret = Lpc546xxUartInit(luart);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: uart init error: %d", __func__, ret);
        return HDF_FAILURE;
    }

    host->priv = luart;
    host->num = luart->busNum;
    host->method = &g_uartHostMethod;
    HDF_LOGI("%s: uart-%u init success!", __func__, host->num);
    return HDF_SUCCESS;
}

void HdfLpc546xxRelease(struct HdfDeviceObject *device)
{
    struct UartHost *host = NULL;

    HDF_LOGI("%s: entry", __func__);
    if (device == NULL) {
        HDF_LOGE("%s: device is null", __func__);
        return;
    }
    host = UartHostFromDevice(device);
    if (host == NULL) {
        HDF_LOGE("%s: host is null", __func__);
        return;
    }
    (void)Lpc546xxUartDeinit(host);
    UartHostDestroy(host);
}

struct HdfDriverEntry g_hdfUartDevice = {
    .moduleVersion = 1,
    .moduleName = "HDF_PLATFORM_UART",
    .Bind = HdfLpc546xxBind,
    .Init = HdfLpc546xxInit,
    .Release = HdfLpc546xxRelease,
};

HDF_INIT(g_hdfUartDevice);
